﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using log4net;
using YetAnotherChat.Common;
using YetAnotherChat.Common.Interfaces;

namespace YetAnotherChat.EntityFrameworkDataAccess
{
    /// <summary>
    /// Base class for repository of type T, which has a table in DB to have corresponding DbSet
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class GenericRepositoryBase<T> : DisposableRepositoryBase, IGenericRepository<T> where T : EntityBase
    {
        protected DbSet<T> _entitySet;

        public GenericRepositoryBase():base()
        {
            _entitySet = _context.Set<T>();
        }

        public virtual T Create(T obj)
        {
            var result = _entitySet.Add(obj);
            //_context.SaveChanges();
            return result;
        }

        public virtual void Update(T obj)
        {
            try
            {
                if (!obj.IsCloned)
                {
                    throw new ArgumentException("obj must be a cloned object");
                }
                //if (_context.Entry(obj).State == System.Data.EntityState.Modified)
                //    _context.SaveChanges();
                _entitySet.Attach(obj);
                _context.Entry(obj).State = System.Data.EntityState.Modified;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                throw;
            }
        }

        public virtual void Delete(T obj)
        {
            try
            {
                if (!obj.IsCloned)
                {
                    throw new ArgumentException("obj must be a cloned object");
                }
                //if (_context.Entry(obj).State != System.Data.EntityState.Deleted)
                //{
                //    _context.Entry(obj).State = System.Data.EntityState.Deleted;
                //    _context.SaveChanges();
                //}
                _entitySet.Attach(obj);
                _context.Entry(obj).State = System.Data.EntityState.Deleted;
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                throw;
            }
        }

        public virtual T Get(Guid id, bool enableTracking= false)
        {
            try
            {
                if (!enableTracking)
                {
                    //disable tracking when get object from DB to add to cache
                    return _entitySet.AsNoTracking().FirstOrDefault(x => x.GuidId == id);
                }
                else
                {
                    return _entitySet.FirstOrDefault(x => x.GuidId == id);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                throw;
            }
        }


        public T[] GetMany(Func<T, bool> where, bool enableTracking = false)
        {
            try
            {
                if (!enableTracking)
                {
                    return _entitySet.AsNoTracking().Where(where).ToArray();
                }
                else 
                {
                    return _entitySet.Where(where).ToArray();
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                throw;
            }
        }

        public void Commit()
        {
            try
            {
                _context.SaveChanges();
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                throw;
            }
        }
    }
}
